package main

import (
	"database/sql"
	"fmt"
	"net/http"
	"regexp"
	"strconv"

	"github.com/PuerkitoBio/goquery"
	_ "github.com/mattn/go-sqlite3"
)

// 数据库配置信息
const (
	DBNAME = "douban_movie"
)

var ID = 0

// 数据结构体
type MovieData struct {
	Title    string `json:"title"`
	Director string `json:"director"`
	Picture  string `json:"picture"`
	Actor    string `json:"actor"`
	Year     string `json:"year"`
	Score    string `json:"score"`
	Quote    string `json:"quote"`
}

var DB *sql.DB

// 数据库初始化
func InitDB() {
	// fmt.Println("????")
	DB, _ = sql.Open("sqlite3", "./"+DBNAME+".db")
	stmt, _ := DB.Prepare("create table if not exists movie_data(id integer primary key, Title text, Director text, Picture text, Actor text, Year text, Score text, Quote text)")
	stmt.Exec()
	DB.SetConnMaxLifetime(10)
	DB.SetMaxIdleConns(5)
	if err := DB.Ping(); err != nil {
		fmt.Println("DB lose connection")
		return
	}
	fmt.Println("DB connected")

}

// 插入数据
func InsertData(m MovieData) bool {
	tx, err := DB.Begin()
	if err != nil {
		fmt.Println("tx fail")
		return false
	}
	stmt, err := tx.Prepare("INSERT INTO movie_data (`id`,`Title`,`Director`,`Picture`,`Actor`,`Year`,`Score`,`Quote`) VALUES  (?,?,?,?,?,?,?,?)")
	if err != nil {
		fmt.Println("prepare fail err", err)
		return false
	}
	_, err = stmt.Exec(ID, m.Title, m.Director, m.Picture, m.Actor, m.Year, m.Score, m.Quote)
	ID += 1
	if err != nil {
		fmt.Println("Exec fail", err)
		return false
	}
	_ = tx.Commit()
	return true
}

// css选择器
// 发送请求 ==> 解析网页 ==> 获取结点 ==> 保存信息

func main() {
	//fmt.Println("777")
	InitDB()
	for i := 0; i < 10; i++ {
		fmt.Printf("正在爬取%d页信息", i)
		Spider(strconv.Itoa(25 * i))
	}
}

// 爬虫客户端
func Spider(page string) {
	// 1.发送请求
	client := http.Client{}
	req, err := http.NewRequest("GET", "https://movie.douban.com/top250?start="+page, nil)
	if err != nil {
		fmt.Println("req err", err)
	}
	// 伪造成浏览器访问，加一些请求头
	req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9")
	// req.Header.Set("Accept-Encoding", "gzip, deflate, br")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
	req.Header.Set("Connection", "keep-alive")
	req.Header.Set("Cache-Control", "max-age=0")
	req.Header.Set("Host", "movie.douban.com")
	req.Header.Set("DNT", "1")
	req.Header.Set("sec-ch-ua", `"Microsoft Edge";v="105", "Not)A;Brand";v="8", "Chromium";v="105"`)
	req.Header.Set("sec-ch-ua-platform", "Windows")
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.53")
	req.Header.Set("Upgrade-Insecure-Requests", "1")
	req.Header.Set("Sec-Fetch-User", "?1")
	req.Header.Set("Sec-Fetch-Site", "none")
	req.Header.Set("Sec-Fetch-Mode", "navigate")
	req.Header.Set("Sec-Fetch-Dest", "document")

	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("req failure", err)
	}
	defer resp.Body.Close()
	// 2.解析网页
	docDetail, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		fmt.Println("xpath failure", err)
	}
	// 3.获取结点信息
	// #content > div > div.article > ol > li:nth-child(1) > div > div.info > div.hd > a > span:nth-child(1)
	// title := docDetail.Find("#content > div > div.article > ol > li:nth-child(1) > div > div.info > div.hd > a > span:nth-child(1)").Text()
	// fmt.Println("title is", title)

	// 获取全部的li标签
	// #content > div > div.article > ol > li:nth-child(1)
	docDetail.Find("#content > div > div.article > ol > li"). // 列表
									Each(func(i int, s *goquery.Selection) { // 在列表里继续找
			// 标题
			title := s.Find("div > div.info > div.hd > a > span:nth-child(1)").Text()
			// 图
			imgSec := s.Find("div > div.pic >  a > img") // img标签 --> src属性
			img, ok := imgSec.Attr("src")
			// 详情
			info := s.Find("div > div.info > div.bd > p:nth-child(1)").Text()
			// 分数
			score := s.Find("div > div.info > div.bd > div > span.rating_num").Text()
			// “废话”
			quote := s.Find("div > div.info > div.bd > p.quote > span").Text()
			var data MovieData
			if ok {
				director, actor, year := InfoSpite(info)
				data.Title = title
				data.Actor = actor
				data.Director = director
				data.Year = year
				data.Score = score
				data.Quote = quote
				data.Picture = img
				// 4.保存信息，插入数据库
				if InsertData(data) {
					fmt.Println("插入成功")
				} else {
					fmt.Println("插入失败")
					return
				}
				// fmt.Println(data)
				// fmt.Println("title:", title)
				// fmt.Println("img:", img)
				// fmt.Println("director:", director)
				// fmt.Println("actor:", actor)
				// fmt.Println("year:", year)
				// // fmt.Println("info:", info)
				// fmt.Println("score:", score)
				// fmt.Println("quote:", quote)
			}
		})
}

// 拆分info中的重要数据
// 导演: 罗伯·莱纳 Rob Reiner   主演: 玛德琳·卡罗尔 Madeline Carroll / 卡...   2010 /  美国 / 剧情 喜剧 爱情
func InfoSpite(info string) (director, actor, year string) {
	directorRe, _ := regexp.Compile(`导演:(.*)主演:`)
	director = string(directorRe.Find([]byte(info)))
	actorRe, _ := regexp.Compile(`主演:(.*)`)
	actor = string(actorRe.Find([]byte(info)))
	yearRe, _ := regexp.Compile(`(\d+)`)
	year = string(yearRe.Find([]byte(info)))
	return
}
